from typing import Iterable, Iterator, TypeVar

from exceptiongroup import ExceptionGroup

from zkl_pyutils_registries.basics import Matcher, MutableRegistry, NotMatched

AnyKey = TypeVar("AnyKey")
AnyValue = TypeVar("AnyValue")
AnyQuery = TypeVar("AnyQuery")
AnyMatch = TypeVar("AnyMatch")


class ListRegistry(MutableRegistry[AnyQuery, AnyKey, AnyValue]):
    def __init__(self, matcher: Matcher[AnyQuery, AnyKey, AnyMatch], entries: Iterable[tuple[AnyKey, AnyValue]] = ()):
        self._matcher = matcher
        self._entries = list[tuple[AnyKey, AnyValue]](entries)

    def register(self, key: AnyKey, value: AnyValue):
        self._entries.append((key, value))

    def _iter_entries_match_result(self, query: AnyQuery) -> Iterator[tuple[bool, AnyMatch, AnyKey, AnyValue]]:
        for key, value in reversed(self._entries):
            try:
                match = self._matcher.match(query, key)
                yield True, match, key, value
            except NotMatched as exception:
                yield False, exception, key, value

    def match(self, query: AnyQuery) -> tuple[AnyValue, ...]:
        results = self._iter_entries_match_result(query)
        matched_results = filter(lambda result: result[0], results)
        sorted_results = sorted(matched_results, key=lambda result: result[1], reverse=True)
        return tuple(result[3] for result in sorted_results)

    def match_one_or_raise(self, query: AnyQuery) -> AnyValue:
        results = self._iter_entries_match_result(query)

        matched_results = []
        failed_results = []
        for result in results:
            if result[0]:
                matched_results.append(result)
            else:
                failed_results.append(result)

        if len(matched_results) == 0:
            raise ExceptionGroup(
                f"Failed to match query={query}",
                [result[1] for result in failed_results])

        sorted_results = sorted(matched_results, key=lambda result: result[1], reverse=True)
        return sorted_results[0][3]
